home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file contains routines to perform semantic optimization for
- * versions (i.e, union queries that pertain to versions).
- * As such, heuristics used to govern the optimization are applicable
- * only to the current implementation of versions, and should not be
- * used as a general purpose semantic optimizer.
- *
- * $Header: /private/postgres/src/planner/prep/RCS/semanopt.c,v 1.12 1991/11/18 17:29:14 mer Exp $
- */
-
- #include <stdio.h>
- #include <strings.h>
- #include "tmp/c.h"
-
- #include "nodes/nodes.h"
- #include "nodes/pg_lisp.h"
- #include "nodes/execnodes.h"
- #include "nodes/plannodes.h"
- #include "nodes/plannodes.a.h"
- #include "nodes/primnodes.h"
- #include "nodes/primnodes.a.h"
- #include "nodes/relation.h"
- #include "nodes/relation.a.h"
-
- #include "parser/parse.h"
- #include "parser/parsetree.h"
- #include "tmp/utilities.h"
- #include "tmp/datum.h"
- #include "utils/log.h"
- #include "utils/lsyscache.h"
-
- #include "planner/internal.h"
- #include "planner/plancat.h"
- #include "planner/planner.h"
- #include "planner/prepunion.h"
- #include "planner/clauses.h"
- #include "planner/semanopt.h"
-
- /*
- * SemantOpt:
- *
- * Given a tlist and qual pair, routine runs through the
- * qualification and substitute all redundent clauses. Eg:
- *
- * TClause : tautological clauses (A.oid = A.oid) becomes (1=1)
- * CClause : contradictory clauses (A.oid = B.oid where A != B)
- * becomes where 1=2
- * EClause : clauses that causes an unwanted existential query.
- * Clauses that have no link back to the targetlist
- * defaults to true (i.e., 1=1).
- * THEREFORE, this will only work for AND clauses.
- *
- * We cannot simply remove CClauses and EClauses from the qualification
- * because then any boolean operations done on the qualification may
- * result in the wrong answer.
- *
- * HOWEVER, I have included a very simplistic heuristic to remove
- * queries with only "and" clauses which have a CClause in them.
- * The routine will punt if it sees any "not" or "or" clauses.
- * In the future, it should also handle those two clauses.
- * I'm going to integrate this is semantop.
- *
- *
- * Returns the optimized qual.
- */
-
- List
- SemantOpt(varlist,rangetable, qual, is_redundent,is_first)
- List varlist,rangetable, qual;
- List *is_redundent;
- int is_first;
- {
- static int punt;
- char *attname;
- List retqual = LispNil;
- Index leftvarno = 0;
- Index rightvarno = 0;
- ObjectId op = 0;
- List tmp_list = LispNil;
- List i = LispNil;
-
- /*
- * NOTE: Routine doesn't handle func clauses.
- */
-
- if (is_first)
- punt = 0; /* to initialize the static variable */
-
- retqual = qual;
-
- if (null(qual))
- return(LispNil);
- else
- if (is_clause(qual)) {
- /* At this stage, union vars are existential */
- if (consp(get_leftop(qual)))
- retqual = MakeTClause();
- else {
- op = get_opno((Oper)get_op(qual));
- if (op == 627) {
- leftvarno = get_varno(get_leftop(qual));
- rightvarno = ConstVarno(rangetable,(Const)get_rightop(qual),&attname);
- /*
- * test for existential clauses first
- */
- if (!member(lispInteger(leftvarno),varlist) &&
- !member(lispInteger(rightvarno),varlist))
- retqual = MakeTClause();
- else
- if (leftvarno == rightvarno) {
- retqual = MakeFClause();
- *is_redundent = LispTrue;
- }
- else
- if (rightvarno > 0 &&
- strcmp(attname,"oid") == 0) {
- List rte1 = LispNil;
- List rte2 = LispNil;
-
- rte1 = nth(leftvarno -1, rangetable);
- rte2 = nth(rightvarno -1, rangetable);
-
- if (CInteger(CADR(CDR(rte1))) != CInteger(CADR(CDR(rte2))))
- retqual = MakeTClause();
- }
- } else
- if (op == 558) { /* oid = */
- AttributeNumber leftattno;
- AttributeNumber rightattno;
- List rte1 = LispNil;
- List rte2 = LispNil;
-
- leftvarno = get_varno(get_leftop(qual));
- rightvarno = get_varno(get_rightop(qual));
- leftattno = get_varattno(get_leftop(qual));
- rightattno = get_varattno(get_rightop(qual));
-
- if (!member(lispInteger(leftvarno),varlist) &&
- !member(lispInteger(rightvarno),varlist))
- retqual = MakeTClause();
- else {
- if (leftattno < 0 && rightattno < 0) {
- /* Comparing the oid field of 2 rels */
- if (leftvarno == rightvarno)
- retqual = MakeTClause();
- else {
- rte1 = nth (leftvarno -1, rangetable);
- rte2 = nth (rightvarno -1, rangetable);
-
- if (strcmp(CString(CADR(rte1)),
- CString(CADR(rte2))) != 0) {
- retqual = MakeFClause();
- *is_redundent = LispTrue;
- }
- }
- }
- }
- } else
- /* Just a normal op clause, check to see if it is existential */
- if (IsA(get_leftop(qual),Var)) {
- leftvarno = get_varno(get_leftop(qual));
- if (IsA(get_rightop(qual),Var)) {
- rightvarno = get_varno(get_rightop(qual));
- if (!member(lispInteger(leftvarno),varlist) &&
- !member(lispInteger(rightvarno),varlist))
- retqual = MakeTClause(); /* this is true only for ANDS */
- }
- if (!member(lispInteger(leftvarno),varlist))
- retqual = MakeTClause();
- }
- }
- } else
- if (and_clause (qual)) {
- foreach(i,get_andclauseargs(qual)) {
- tmp_list = nappend1(tmp_list,SemantOpt(varlist,
- rangetable,
- CAR(i),
- is_redundent,
- 0));
- }
- retqual = make_andclause(tmp_list);
- }
- else
- if (or_clause (qual)) {
- punt = 1;
- foreach(i,get_orclauseargs(qual)) {
- tmp_list = nappend1(tmp_list,SemantOpt(varlist,
- rangetable,
- CAR(i),
- is_redundent,
- 0));
- }
- retqual = make_orclause(tmp_list);
- }
- else
- if (not_clause (qual)) {
- punt = 1;
- retqual = make_notclause(SemantOpt(varlist,
- rangetable,
- get_notclausearg(qual),
- is_redundent,
- 0));
- }
- if (punt)
- *is_redundent = LispNil;
-
- return (retqual);
-
- }
-
- /*
- * SemantOpt2 optimizes redundent joins.
- *
- * The theory behind this is that given to primary keys, eg OIDs
- * a qual. of the form :
- * ... from e in emp where e.oid = emp.oid
- * can be optimized into scan on emp, instead of a join.
- */
- List
- SemantOpt2(rangetable,qual,modqual,tlist)
- List rangetable,qual,modqual,tlist;
- {
- AttributeNumber leftattno = 0;
- AttributeNumber rightattno = 0;
- List rte1 = LispNil;
- List rte2 = LispNil;
- Index leftvarno = 0;
- Index rightvarno = 0;
- List i = LispNil;
-
- if (null(qual))
- return(LispNil);
- else
- if (is_clause(qual) && get_opno((Oper)get_op(qual)) == 558) {
- /*
- * Now to test if they are the same relation.
- */
-
- leftvarno = get_varno(get_leftop(qual));
- rightvarno = get_varno(get_rightop(qual));
- leftattno = get_varattno(get_leftop(qual));
- rightattno = get_varattno(get_rightop(qual));
-
- if (leftattno < 0 && rightattno < 0) {
- /* Comparing the oid field of 2 rels */
-
- rte1 = nth(leftvarno - 1, rangetable);
- rte2 = nth(rightvarno - 1, rangetable);
-
- if (strcmp(CString(CADR(rte1)),
- CString(CADR(rte2))) == 0 &&
- leftvarno != rightvarno) {
- /* Remove the redundent join */
- List temp = MakeTClause();
- CAR(qual) = CAR(temp);
- CDR(qual) = CDR(temp);
- replace_tlist(rightvarno,leftvarno,tlist);
- replace_varnodes(rightvarno,leftvarno,modqual);
- }
- }
- }
- else
- if (and_clause(qual))
- foreach(i,get_andclauseargs(qual))
- modqual = SemantOpt2(rangetable,CAR(i),modqual,tlist);
- else
- if (or_clause(qual))
- foreach(i,get_orclauseargs(qual))
- modqual = SemantOpt2(rangetable,CAR(i),modqual,tlist);
- else
- if (not_clause(qual))
- modqual = SemantOpt2(rangetable,CDR(qual), modqual,tlist);
-
- return(modqual);
- }
-
- void
- replace_tlist(left,right,tlist)
- Index left,right;
- List tlist;
- {
- List i = LispNil;
- List tle = LispNil;
- List leftop = LispNil;
- List rightop = LispNil;
-
- foreach(i,tlist) {
- tle = tl_expr(CAR(i));
-
- if (IsA(tle,Var)) {
- if (get_varno((Var)tle) == right) {
- set_varno((Var)tle,left);
- set_varid((Var)tle, lispCons(lispInteger(left),
- lispCons(CADR(get_varid((Var)tle)),
- LispNil)) );
- }
- } else
- if (is_clause(tle))
- replace_tlist(left,right,CDR(tlist) );
-
- }
-
- }
-
- void
- replace_varnodes(left,right,qual)
- Index left,right;
- List qual;
- {
- List leftop = LispNil;
- List rightop = LispNil;
- List i = LispNil;
-
- if (null(qual));
- if (is_clause(qual)) {
- leftop = (List)get_leftop(qual);
- rightop = (List)get_rightop(qual);
-
- if (IsA(leftop,Var))
- if (get_varno((Var)leftop) == right) {
- set_varno((Var)leftop,left);
- set_varid((Var)leftop, lispCons(lispInteger(left),
- lispCons(CADR(get_varid((Var)leftop)),
- LispNil)) );
- } else
- if (IsA(rightop,Var))
- if (get_varno((Var)rightop) == right) {
- set_varno((Var)rightop,left);
- set_varid((Var)rightop, lispCons(lispInteger(left),
- lispCons(CADR(get_varid((Var)rightop)),
- LispNil)) );
- }
- }
- else
- if (and_clause(qual))
- foreach(i,get_andclauseargs(qual))
- replace_varnodes(left,right,CAR(i));
- else
- if (or_clause(qual))
- foreach(i,get_orclauseargs(qual))
- replace_varnodes(left,right,CAR(i));
- else
- if (not_clause(qual))
- replace_varnodes(left,right,CDR(qual));
-
- }
- /*
- * Routine that runs through the tlist and qual pair
- * and collect all varnos that are not existential.
- * returns a list of those varnos.
- */
-
- List
- find_allvars (root,rangetable,tlist,qual)
- List root,rangetable,tlist,qual;
- {
- List varlist = LispNil;
- List i = LispNil;
- List tle = LispNil;
- List expr = LispNil;
- int current_nvars;
- int prev_nvars = 0;
-
- if (CAtom(root_command_type_atom(root)) != RETRIEVE)
- varlist = nappend1(varlist, root_result_relation(root));
-
- foreach(i, tlist) {
- tle = CAR(i);
- expr = tl_expr(tle);
- /*
- * Assume that each (non const) expr is a varnode by this time.
- */
- if (IsA(expr,Var)) {
- if (!member(lispInteger(get_varno((Var)expr)),varlist))
- varlist = nappend1(varlist, lispInteger(get_varno((Var)expr)));
- }
- }
- current_nvars = length(varlist);
-
- while(current_nvars > prev_nvars) {
- prev_nvars = current_nvars;
- varlist = update_vars(rangetable,varlist,qual);
- current_nvars = length(varlist);
- }
- return(varlist);
- }
-
- /*
- * Simply goes through the qual once and
- * add any new varnos (rels) that participate in a
- * join.
- * A rel. participates in the query if there is a link back to
- * the target relations.
- */
-
- List
- update_vars(rangetable,varlist,qual)
- List rangetable,varlist,qual;
- {
- List leftop;
- List rightop;
- char *attname;
- ObjectId op = 0;
- Index leftvarno = 0;
- Index rightvarno = 0;
- List tmp = LispNil;
-
- if (null(qual))
- return(varlist);
- else
- if (is_clause(qual)) {
- op = get_opno((Oper)get_op(qual));
- if (!consp(get_leftop(qual))) { /* ignore union vars at this point */
- if (op == 627) { /* Greg's horrendous not-in op */
- leftvarno = get_varno(get_leftop(qual));
- rightvarno = ConstVarno(rangetable,(Const)get_rightop(qual),&attname);
-
- if (member(lispInteger(leftvarno),varlist)) {
- if (rightvarno != 0 && !member(lispInteger(rightvarno),varlist))
- varlist = nappend1(varlist,lispInteger(rightvarno));
- }
- else
- if (member(lispInteger(rightvarno),varlist))
- if (!member(lispInteger(leftvarno),varlist))
- varlist = nappend1(varlist,lispInteger(leftvarno));
- } else {
- leftop = (List)get_leftop(qual);
- rightop = (List)get_rightop(qual);
- if (IsA(leftop,Var)) {
- if (IsA(rightop,Var)) {
- if (member(lispInteger(get_varno((Var)leftop)),varlist)) {
- if (!member(lispInteger(get_varno((Var)rightop)),varlist))
- varlist =
- nappend1(varlist,lispInteger(get_varno((Var)rightop)));
- }
- else
- if (member(lispInteger(get_varno((Var)rightop)),varlist))
- if (!member(lispInteger(get_varno((Var)leftop)),varlist))
- varlist =
- nappend1(varlist,lispInteger(get_varno((Var)leftop)));
- }
- } /* leftop var */
- } /*else */
- }
- } else /* is_clause */
- if (and_clause(qual)) {
- foreach(tmp, get_andclauseargs(qual))
- varlist = update_vars(rangetable,
- varlist,
- CAR(tmp));
- }
- else
- if (or_clause(qual)) {
- foreach (tmp, get_orclauseargs(qual))
- varlist = update_vars(rangetable,
- varlist,
- CAR(tmp));
- }
- else
- if (not_clause(qual))
- varlist = update_vars(rangetable,
- varlist,
- get_notclausearg(qual));
-
- return(varlist);
- }
-
- /*
- * This routine is necessary thanks to Greg's not-in operator.
- * Given a const node, routine returns the varno of the relation
- * referenced or 0 if relation is not in the rangetable.
- */
-
- Index
- ConstVarno(rangetable,constnode,attname)
- List rangetable;
- Const constnode;
- char **attname;
- {
- static char tmpstring[32];
- char *ptr;
- char *relname;
- List i = LispNil;
- Index position = 0; /* should be quite safe since varnos start from 1. */
-
- if (!IsA(constnode,Const))
- elog(WARN, "Arg. is not a const node.");
- else {
- ptr = DatumGetPointer(get_constvalue(constnode));
- strcpy (tmpstring, ptr);
- relname = (char *)strtok(tmpstring, ".");
- *attname = (char *)strtok(NULL,".");
-
- foreach (i, rangetable) {
- position += 1;
- if (strcmp(relname, CString(CADR(CAR(i)))) == 0)
- return (position);
- }
- }
- return(0);
-
- }
-
- /*
- * Routine returns a clause of the form 1=1
- */
- List
- MakeTClause()
- {
- Oper newop;
- Const leftconst;
- Const rightconst;
- ObjectId objid = 96;
- ObjectId rettype = 16;
- int opsize = 0;
-
- newop = MakeOper(objid, InvalidObjectId, 0,rettype,opsize,NULL);
- leftconst = MakeConst(23, 4, Int32GetDatum(1), 0, 1);
- rightconst = MakeConst(23, 4, Int32GetDatum(1), 0, 1);
-
- return(make_opclause(newop,(Var)leftconst,(Var)rightconst));
-
- }
-
- /*
- * Routine returns a clause of the form 1=2
- */
- List
- MakeFClause()
- {
- Oper newop;
- Const leftconst;
- Const rightconst;
- ObjectId objid = 96;
- ObjectId rettype = 16;
- int opsize = 0;
-
- newop = MakeOper(objid, InvalidObjectId, 0,rettype,opsize,NULL);
- leftconst = MakeConst(23, 4, Int32GetDatum(1), 0, false);
- rightconst = MakeConst(23, 4,Int32GetDatum(2), 0, false);
-
- return(make_opclause(newop,(Var)leftconst,(Var)rightconst));
-
- }
-